package com.example.Cairodio;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.drawable.Drawable;

import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.example.entities.Place;
import com.example.entities.Road;
import com.example.entities.RoadProvider;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;

public class CairodioMapActivity extends MapActivity {

	CairodioDbAdapter dbHelper;

	LinearLayout linearLayout;
	MapView mapView;

	static Context context;

	public List<Overlay> mapOverlays;
	private Drawable mapDrawable;
	ItemizedOverlay itemizedoverlay, tourOverlay1, tourOverlay2, tourOverlay3;
	public int placesLength;

	MapController mapController;
	MyLocationOverlay myLocationOverlay;
	public GeoPoint myCurrentLocation;
	private Road mRoad;

	public static Intent intentMyService;
	ComponentName service;
	BroadcastReceiver receiver;
	String GPS_FILTER = "guc.action.GPS_LOCATION";

	public static Intent intent;
	Button nearby;
	boolean placesNearby = false;
	double la;
	double lo;

	Button tourBtn1, tourBtn2, tourBtn3;
	double currentLat, currentLong;
	public boolean tourIsActive, stopUpdate;
	public boolean tourSelected = false;
	boolean goToTour = false;

	static final int _TourActivityIntent = 0;
	static final int _AudioClipsActivityIntent = 1;
	static final int _NearbyPlaces = 2;

	ArrayList<Place> tourPlacesList;
	int tourNumber;

	/**
	 * This method is called when the application starts, it initializes the
	 * global variables and calls drawAllPlaces()
	 * 
	 * @author Mariam
	 */

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		context = getApplicationContext();

		intent = new Intent(CairodioMapActivity.this, BackgroundService.class);
		// stopService(intent);

		// Get Database
		dbHelper = ((CairodioApplication) getApplication()).getAdapter();

		mapView = (MapView) findViewById(R.id.mapview);

		intentMyService = new Intent(this, GpsService.class);
		service = startService(intentMyService);
		IntentFilter mainFilter = new IntentFilter(GPS_FILTER);
		receiver = new MyMainLocalReceiver();
		registerReceiver(receiver, mainFilter);

		mapController = mapView.getController();

		myLocationOverlay = new MyLocationOverlay(this, mapView);
		mapView.getOverlays().add(myLocationOverlay);
		myLocationOverlay.enableMyLocation();
		mapView.postInvalidate();

		nearby = (Button) findViewById(R.id.nearby);

		nearby.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {

				if (placesNearby) {
					Bundle b = new Bundle();
					b.putDouble(
							getString(R.string.nearby_places_activity_latitude),
							la);
					b.putDouble(
							getString(R.string.nearby_places_activity_longitude),
							lo);
					Intent intentNearbyPlaces = new Intent(
							CairodioMapActivity.this,
							NearbyPlacesActivity.class);
					intentNearbyPlaces.putExtras(b);
					startActivityForResult(intentNearbyPlaces, _NearbyPlaces);
				} else {
					Toast.makeText(getApplicationContext(),
							R.string.cairodio_map_activity_nearby_error,
							Toast.LENGTH_SHORT).show();
				}

			}
		});

		drawAllPlaces();

		// Tour buttons
		tourBtn1 = (Button) findViewById(R.id.Tour1);
		tourBtn2 = (Button) findViewById(R.id.Tour2);
		tourBtn3 = (Button) findViewById(R.id.Tour3);

		tourBtn1.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				tourNumber = 1;
				setTour();
				(findViewById(R.id.toursBar)).setVisibility(View.GONE);
				initialMap();

			}
		});

		tourBtn2.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				tourNumber = 2;
				setTour();
				(findViewById(R.id.toursBar)).setVisibility(View.GONE);
				initialMap();
			}
		});

		tourBtn3.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				tourNumber = 3;
				setTour();
				(findViewById(R.id.toursBar)).setVisibility(View.GONE);
				initialMap();
			}
		});
	}

	// Drawing the path between an array of places
	public void drawPath(ArrayList<Place> places) {

		for (int i = 0; i < places.size() - 1; i++) {
			setRoute(places.get(i).getLatitude(), places.get(i).getLongitude(),
					places.get(i + 1).getLatitude(), places.get(i + 1)
							.getLongitude());
		}
	}

	/**
	 * @author Mona
	 * 
	 *  This method is called upon selecting a tour.
	 *  It creates an intent of the Tour Activity and passes the necessary
	 *  information needed to creat the tour.
	 *  
	 */
	public void setTour() {

		this.tourSelected = true;
		// (findViewById(R.id.goToTour)).setVisibility(1);
		Bundle b = new Bundle();
		b.putBoolean("newTour", true);
		b.putInt("tourNumber", tourNumber);
		b.putDouble("Latitude", currentLat);
		b.putDouble("Longitude", currentLong);
		Intent intentTourActivity = new Intent(CairodioMapActivity.this,
				TourActivity.class);
		intentTourActivity.putExtras(b);
		startActivityForResult(intentTourActivity, _TourActivityIntent);
	}

	/**
	 * @author Mariam
	 * 
	 * This method takes the lat and long of the a source place 
	 * and the lat and long of the destination place and connect to the server to get 
	 * the route coordinates.
	 * @param fromLat
	 * @param fromLon
	 * @param toLat
	 * @param toLon
	 */
	public void setRoute(final double fromLat, final double fromLon,
			final double toLat, final double toLon) {
		new Thread() {
			@Override
			public void run() {
				String url = RoadProvider
						.getUrl(fromLat, fromLon, toLat, toLon);
				InputStream is = getConnection(url);
				mRoad = RoadProvider.getRoute(is);
				mHandler.sendEmptyMessage(0);
			}
		}.start();
	}

	/**
	 * @author Mariam
	 * 
	 * This handler makes a new insatnce of Mapoverlay class
	 * and adds the route on the map
	 */
	Handler mHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			MapOverlay mapOverlay = new MapOverlay(mRoad, mapView);
			List<Overlay> listOfOverlays = mapView.getOverlays();
			// listOfOverlays.clear();
			listOfOverlays.add(mapOverlay);
			mapView.invalidate();
		};
	};

	/**
	 * @author Mariam
	 * 
	 * This method takes a url and establish the connection
	 * @param url
	 * @return
	 */
	private InputStream getConnection(String url) {
		InputStream is = null;
		try {
			URLConnection conn = new URL(url).openConnection();
			is = conn.getInputStream();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return is;
	}

	/**
	 * 
	 * This method gets all the touristic places from the database and put them
	 * in the map overlay and show them.
	 * 
	 * @author Mariam
	 */

	@SuppressWarnings("static-access")
	public void drawAllPlaces() {

		placesLength = 0;

		mapView.setBuiltInZoomControls(true);

		mapOverlays = mapView.getOverlays();
		mapDrawable = this.getResources().getDrawable(R.drawable.pocdcast);
		itemizedoverlay = new ItemizedOverlay(mapDrawable,
				CairodioMapActivity.this);

		Cursor c = dbHelper.getAllPlaces();
		c.moveToFirst();
		while (!c.isAfterLast()) {

			double latitude = c.getDouble(c.getColumnIndex(dbHelper.colLat));
			double longitude = c.getDouble(c.getColumnIndex(dbHelper.colLong));

			int lat = (int) (latitude * 1E6);
			int lng = (int) (longitude * 1E6);

			GeoPoint point = new GeoPoint(lat, lng);
			OverlayItem overlayitem = new OverlayItem(point, c.getString(c
					.getColumnIndex(dbHelper.colName)), Integer.toString(c
					.getInt(c.getColumnIndex(dbHelper.colPlaceID))));
			itemizedoverlay.addOverlay(overlayitem);
			mapOverlays.add(itemizedoverlay);

			Log.e("DATABASE", c.getString(c.getColumnIndex(dbHelper.colName)));
			c.moveToNext();
			placesLength++;
		}

	}

	/**
	 * @author Mariam
	 * 
	 * This method remove all touristics places from the map
	 */
	public void removeAllplaces() {
		for (int i = 0; i < placesLength; i++) {
			itemizedoverlay.removeItem(placesLength - 1 - i);
		}

		mapView.postInvalidate();
	}

	/**
	 * @author Mariam
	 * 
	 * This method draw the 3 tours options on the map
	 * and show their buttons
	 */
	public void drawTourOptions() {

		stopUpdate = true;

		nearby.setVisibility(View.GONE);
		(findViewById(R.id.chooseTour)).setVisibility(1);

		mapView.setBuiltInZoomControls(false);

		GeoPoint cairo = new GeoPoint(30000000, 31200000);
		mapController.animateTo(cairo);
		mapController.setZoom(12);

		removeAllplaces();

		mapOverlays = mapView.getOverlays();

		Drawable tour1Drawable = this.getResources().getDrawable(
				R.drawable.sphinx_tour);
		Drawable tour2Drawable = this.getResources().getDrawable(
				R.drawable.memphis_tour);
		Drawable tour3Drawable = this.getResources().getDrawable(
				R.drawable.citycenter_tour);

		tourOverlay1 = new ItemizedOverlay(tour1Drawable,
				CairodioMapActivity.this);
		tourOverlay2 = new ItemizedOverlay(tour2Drawable,
				CairodioMapActivity.this);
		tourOverlay3 = new ItemizedOverlay(tour3Drawable,
				CairodioMapActivity.this);

		GeoPoint point1 = new GeoPoint(29976000, 31134000);
		GeoPoint point2 = new GeoPoint(29870000, 31220000);
		GeoPoint point3 = new GeoPoint(30050000, 31210000);

		OverlayItem overlayitem1 = new OverlayItem(point1, "", "");
		OverlayItem overlayitem2 = new OverlayItem(point2, "", "");
		OverlayItem overlayitem3 = new OverlayItem(point3, "", "");

		tourOverlay1.addOverlay(overlayitem1);
		tourOverlay2.addOverlay(overlayitem2);
		tourOverlay3.addOverlay(overlayitem3);

		mapOverlays.add(tourOverlay1);
		mapOverlays.add(tourOverlay2);
		mapOverlays.add(tourOverlay3);

		mapView.postInvalidate();

	}

	/**
	 * @author Mariam
	 * 
	 * This method removes the tours options from the map
	 */
	public void removeTourOptions() {

		if (tourOverlay1.size() > 0) {
			tourOverlay1.removeItem(0);
			tourOverlay2.removeItem(0);
			tourOverlay3.removeItem(0);

			mapView.postInvalidate();
		}
	}

	/**
	 * @author Mariam
	 * 
	 * This method returns to map's initial state (on creation state)
	 * with only the touristic places and the mylocation
	 */
	public void initialMap() {
		removeTourOptions();

		List<Overlay> listOfOverlays = mapView.getOverlays();
		listOfOverlays.clear();
		mapView.getOverlays().add(myLocationOverlay);
		myLocationOverlay.enableMyLocation();
		myLocationOverlay.runOnFirstFix(new Runnable() {
			public void run() {
				myCurrentLocation = myLocationOverlay.getMyLocation();
			}
		});

		drawAllPlaces();

		nearby.setVisibility(1);
		(findViewById(R.id.chooseTour)).setVisibility(View.GONE);

		stopUpdate = false;
	}

	public void onStart() {
		super.onStart();
		stopService(intent);
		receiver.clearAbortBroadcast();
		// Toast.makeText(this,
		// "CairodioMapActivity: onRestart",Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onPause() {
		super.onPause();
		// stopService(intentMyService);
		receiver.abortBroadcast();
		// intent = new Intent(CairodioMapActivity.this,
		// BackgroundService.class);
		startService(intent);
		// Toast.makeText(this,
		// "CairodioActivity: onPause",Toast.LENGTH_SHORT).show();

	}

	@Override
	public void onStop() {
		super.onStop();
		// Toast.makeText(this,
		// "CairodioMapActivity: onStop",Toast.LENGTH_SHORT).show();

	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		stopService(intent);
		System.runFinalizersOnExit(true);
		System.exit(0);
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	private class MyMainLocalReceiver extends BroadcastReceiver {

		/**
		 * This method is a receiver that receives updates from the gps with
		 * loction coordinates, and update the map view.
		 * 
		 * It performs a check if there are any touristic places nearby if yes
		 * it directs to Khaled's NearbyPlaces Activity
		 * 
		 * @author Mariam
		 * 
		 */

		@Override
		public void onReceive(Context localContext, Intent callerIntent) {
			currentLat = callerIntent.getDoubleExtra("latitude", -1);
			currentLong = callerIntent.getDoubleExtra("longitude", -1);
			Log.e("MAIN>>>", Double.toString(currentLat));
			Log.e("MAIN>>>", Double.toString(currentLong));
			int lat = (int) (currentLat * 1E6);
			int lng = (int) (currentLong * 1E6);

			if (dbHelper.getNearByPlacesName(currentLat, currentLong).size() > 0) {

				la = currentLat;
				lo = currentLong;

				if (!placesNearby)
					toggleNearbyButton();

			} else {
				if (placesNearby)
					toggleNearbyButton();
			}

			myCurrentLocation = new GeoPoint(lat, lng);

			if (!stopUpdate) {
				mapController.animateTo(myCurrentLocation);
				// mapController.setZoom(13);
				mapView.invalidate();
			}
		}

	}
	
	/**
	 * @author Mariam
	 * 
	 * This method animates the map to the user's loaction 
	 * if he lost himself 
	 */

	public void findMe() {

		myLocationOverlay.runOnFirstFix(new Runnable() {
			public void run() {
				myCurrentLocation = myLocationOverlay.getMyLocation();
				if (myCurrentLocation != null) {
					mapController.animateTo(myCurrentLocation);
					mapController.setZoom(13);
					mapView.invalidate();
				} else {
					Toast.makeText(getApplicationContext(),
							R.string.cairodio_map_activity_location_error,
							Toast.LENGTH_SHORT).show();
				}
			}
		});

	}

	/**
	 * This method creates the option menu
	 * 
	 * @author Mariam
	 */

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu, menu);
		return true;
	}

	/**
	 * @author Mona
	 * 
	 *  This method is called everytime the menu button is clicked.
	 *  It is used to add/remove menu items when needed.
	 *  
	 */
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {

		if (tourSelected && !goToTour) {
			menu.add(Menu.NONE, 11111, Menu.NONE,
					R.string.menu_start_go_to_tour);
			goToTour = true;
		} else if (!tourSelected)
			menu.removeItem(11111);

		return super.onPrepareOptionsMenu(menu);

	}

	/**
	 * This method directs to displayQuitAlert() if the user clicked on Quit
	 * icon
	 * 
	 * @author Mariam
	 */

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.quitIcon:
			displayQuitAlert();
			return true;
		case R.id.tourIcon: { // Starting the tour
			if (!tourIsActive) {
				(findViewById(R.id.toursBar)).setVisibility(1);
				item.setTitle(R.string.menu_stop_tour);
				drawTourOptions();
			} else { // Stopping the tour
				(findViewById(R.id.toursBar)).setVisibility(View.GONE);
				item.setTitle(R.string.menu_start_tour);
				this.tourSelected = false;
				goToTour = false;
				initialMap();

			}
			tourIsActive = !tourIsActive;
		}
			return true;

		// Go To Tour Option
		case 11111: {
			Toast.makeText(this, "go to tour", Toast.LENGTH_SHORT).show();
			Bundle b = new Bundle();
			b.putBoolean("newTour", false);
			b.putInt("tourNumber", tourNumber);
			b.putDouble("Latitude", currentLat);
			b.putDouble("Longitude", currentLong);
			b.putSerializable("TourPlaces", tourPlacesList);
			Intent intentTourActivity = new Intent(CairodioMapActivity.this,
					TourActivity.class);
			intentTourActivity.putExtras(b);
			startActivityForResult(intentTourActivity, _TourActivityIntent);
		}
			return true;

		case R.id.findMe:
			findMe();
			return true;

		}
		return false;
	}

	/**
	 * This method display the message of "Are you sure you want to quit?" if
	 * yes was chosen the application is terminated if no was chosen nothing
	 * happens
	 * 
	 * @author Mariam
	 */
	public void displayQuitAlert() {

		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(R.string.cairodio_map_activity_quit_alert)
				.setCancelable(false)
				.setPositiveButton(R.string.cairodio_map_activity_positive,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								CairodioMapActivity.this.finish();
							}
						})
				.setNegativeButton(R.string.cairodio_map_activity_negative,
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog, int id) {
								dialog.cancel();
							}
						});
		AlertDialog alert = builder.create();
		alert.show();

	}

	/**
	 * @Shokry This method is called when the reciever detects nearby places
	 *         using the method getNearbyPlaces(), This method displays an
	 *         alert, if yes is chosen, it goes to the nearbyPlaces Activity
	 * 
	 * @param la
	 * @param lo
	 */
	public void toggleNearbyButton() {
		// TODO Auto-generated method stub

		if (placesNearby) {
			nearby.setClickable(false);
			nearby.setBackgroundResource(R.layout.btn_red);
		} else {
			nearby.setClickable(true);
			nearby.setBackgroundResource(R.layout.btn_green);
		}

		placesNearby = !placesNearby;

	}

	// Method to handle from which activity we returned
	@SuppressWarnings("unchecked")
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {

		switch (requestCode) {
		case _TourActivityIntent:
			if (resultCode == RESULT_OK) {
				tourPlacesList = new ArrayList<Place>();
				tourPlacesList = (ArrayList<Place>) data.getExtras()
						.getSerializable("TourPlaces");
				Toast.makeText(this,
						"Retrieved array of size: " + tourPlacesList.size(),
						Toast.LENGTH_SHORT).show();
				drawPath(tourPlacesList);
				break;
			}
		case _AudioClipsActivityIntent:
			if (resultCode == RESULT_OK) {

				break;
			}
		}
	}

}

/**
 * 
 * @author Mariam
 *
 *
 *This class is called when the coordinates are already there from the server 
 *and the route have to be drawn.
 *the constructer takes Road to be drawn and a mapview to be drawn on it. 
 */
class MapOverlay extends com.google.android.maps.Overlay {
	Road mRoad;
	ArrayList<GeoPoint> mPoints;

	public MapOverlay(Road road, MapView mv) {
		mRoad = road;
		if (road.mRoute.length > 0) {
			mPoints = new ArrayList<GeoPoint>();
			for (int i = 0; i < road.mRoute.length; i++) {
				mPoints.add(new GeoPoint((int) (road.mRoute[i][1] * 1000000),
						(int) (road.mRoute[i][0] * 1000000)));
			}
			int moveToLat = (mPoints.get(0).getLatitudeE6() + (mPoints.get(
					mPoints.size() - 1).getLatitudeE6() - mPoints.get(0)
					.getLatitudeE6()) / 2);
			int moveToLong = (mPoints.get(0).getLongitudeE6() + (mPoints.get(
					mPoints.size() - 1).getLongitudeE6() - mPoints.get(0)
					.getLongitudeE6()) / 2);
			GeoPoint moveTo = new GeoPoint(moveToLat, moveToLong);

			MapController mapController = mv.getController();
			mapController.animateTo(moveTo);
			mapController.setZoom(15);
		}
	}

	@Override
	public boolean draw(Canvas canvas, MapView mv, boolean shadow, long when) {
		super.draw(canvas, mv, shadow);
		drawPath(mv, canvas);
		return true;
	}

	public void drawPath(MapView mv, Canvas canvas) {
		int x1 = -1, y1 = -1, x2 = -1, y2 = -1;
		Paint paint = new Paint();
		paint.setColor(Color.GREEN);
		paint.setStyle(Paint.Style.STROKE);
		paint.setStrokeWidth(3);
		for (int i = 0; i < mPoints.size(); i++) {
			Point point = new Point();
			mv.getProjection().toPixels(mPoints.get(i), point);
			x2 = point.x;
			y2 = point.y;
			if (i > 0) {
				canvas.drawLine(x1, y1, x2, y2, paint);
			}
			x1 = x2;
			y1 = y2;
		}
	}
}
